#!/bin/bash
# 该脚本用于对所给文件名进行重命名、以及创建新的目录.说明如下:
# - 在所给文件名前面加上数字,且数字会递增.
# - 去掉所给文件名前面的数字、以及数字后面的 '-' 字符.
# - 把所给文件名前面的数字加 1.
# - 基于所给的目录名创建新的目录,会在目录名前面自动加上数字
#   和 '-' 字符. 数字值是当前目录下子目录名的最大数字加上 1.

show_help()
{
printf "USAGE
    $(basename $0) option [dirname1 ... [dirnamen]]
OPTIONS
    option: 必须提供的选项参数.每次只能提供一个选项. 支持的选项如下:
        -h: 打印这个帮助信息,然后退出脚本.
        -p: 在所给的文件名前面加上数字,且数字会递增. 例如,所给的
            文件名是 a b c,则把对应文件名分别重命名为 1-a 2-b 3-c.
        -r: 去掉所给文件名前面的数字、以及数字后面的 '-' 字符.例如,
            所给的文件名是 1-a 22-b 333-c,则会分别重命名为 a b c.
            如果所给文件名的开头不符合 '数字-' 这个格式,不做处理.
        -i: 把所给文件名前面的数字加 1,并重命名该文件.例如,所给的
            文件名是 1-a 22-b 333-c,则会分别重命名为 2-a 23-b 334-c.
            如果所给文件名的开头不符合 '数字-' 这个格式,不做处理.
        -m: 基于所给的目录名创建新的目录,会在目录名前面自动加上数字
            和 '-' 字符.数字值是当前目录下子目录名的最大数字加上 1.
    dirname1 ... dirnamen: 可选的文件名参数
        对于 -p/-r/-i 参数来说,后面提供的参数就是要处理的文件名参数.
        如果没有文件名参数,默认使用当前目录下的子目录名.

        对应 -m 参数来说,后面提供的参数是要创建的目录名后缀部分.
        实际创建的目录名会在所给目录名参数前面加上数字和 '-' 字符.
        <注意>: 提供 -m 选项时,后面必须提供的目录名参数.
NOTE
    -p 是 prefix 的简写. -r 是 remove 的简写. -i 是 increase 的简写.
    -m 是 mdkir 的简写.可以按照这些单词含义来帮助记忆每个选项的作用.
"
}

# 该函数重命名所给的每一个文件名,会在文件名前面添加数字.数字从 1 开始
# 递增. 例如,所给的文件名参数是 a b c,则会分别重命名为 01-a 02-b 03-c.
prefix_digit_to_filename()
{
    if [ $# -eq 0 ]; then
        echo "Usage: $FUNCNAME filename1 [filename2 ...[filenamen]]"
        return 1
    fi
    # 在文件名前面添加的数字从 1 开始.如有需要,可以改成从 0 开始.
    local number=0
    local fileNamePre
    local filename
    for filename in "$@"; do
        # 不判断文件名是否已经以数字开头.直接在文件名前面添加数字.
        ((number++))
        if [ ${number} -lt 10 ]; then
         fileNamePre="0${number}";
        fi
        mv -v "${filename}" "${fileNamePre}-${filename}"
    done
}

# 该函数重命名所给的每一个文件名,会去掉文件名前面的数字和 '-' 字符.
# 例如,所给的文件名参数是 1-a 22-b 333-c,则会分别重命名为 a b c.
remove_filename_prefix_digit()
{
    if [ $# -eq 0 ]; then
        echo "Usage: $FUNCNAME filename1 [filename2 ...[filenamen]]"
        return 1
    fi

    # 在 Linux 中, mv 命令重命名每次只能处理一个文件名.而 rename 命令
    # 可以处理多个文件,且可以使用正则表达式来表示要替换的文件名内容.
    # rename 命令使用 perl 正则表达式. 可以使用 \d 匹配任意一个数字.
    # 用 + 匹配一次或多次前一个字符.则 ^\d+ 匹配文件名开头的所有数字.
    # 在 ^\d+ 后面的 - 对应 '-' 字符自身.要求在数字后面跟着 '-' 字符.
    # 下面的命令把文件名开头的数字和 '-' 字符替换为空,从而去掉数字.
    rename -v 's/^\d+-//' "$@"
}

# 该函数重命名所给的每一个文件名,会把所给文件名开头的数字加 1. 例如,
# 所给的文件名参数是 1-a 22-b 333-c,则会分别重命名为 2-a 23-b 334-c.
increase_filename_number()
{
    if [ $# -eq 0 ]; then
        echo "Usage: $FUNCNAME filename1 [filename2 ...[filenamen]]"
        return 1
    fi

    # 在 perl 的 s///e 表达式中,添加最后面的 e 指定评估表达式.如果
    # 不加最后面的 e,只会进行字符串拼接,而不是进行算术运算. 使用
    # 小括号把 \d+ 括起来,以便后面通过 $1 反向引用到 \d+ 匹配的内容.
    rename -v 's/^(\d+)-/sprintf("%d-",$1+1)/e' "$@"
}

# 基于所给的目录名创建新的目录.目录名前面会加上数字,数字后面跟着
# '-' 字符. 具体的数字值会从当前子目录名开头最大的数字往上递增.
# 例如,当前目录下,开头数字最大的子目录名是 333-c. 调用该函数时,
# 传入的目录名参数是 d,则会创建一个新的 334-d 目录.
mkdir_digit_filename()
{
    if [ $# -eq 0 ]; then
        echo "出错: 使用 -m 选项时,后面必须提供要创建的目录名."
        return 1
    fi

    # 打开 bash 的 extglob shell 选项,才能在路径名扩展中使用
    # +(pattern-list) 来表示匹配一次或多次所给模式.这个选项的状态跟
    # 所在的shell有关.在当前 shell 打开该选项,不影响父 shell 的状态.
    # 可以使用 shopt -p extglob 来查看该选项的状态.默认应该是开的.
    # 这里主动打开该选项,可以提升移植性,避免有些Linux版本没有打开.
    shopt -s extglob
    # 在打开 extglob 选项后, +([0-9])-*/ 会扩展为当前目录下,以
    # 数字开头、且后面跟着 '-' 字符的子目录名,不包含文本文件名.
    # 使用 ls -d 命令逐行打印扩展得到的子目录名,并传递给 awk 处理.
    # awk 以 '-' 分割所给字符串,获取子目录名前面的数字,输出最大的数字.
    local maxnumber=`ls -d +([0-9])-*/ | \
        awk -F '-' '{ if (x < $1) x = $1 } END { print x }'`

    local dirname
    # 在所给的每一个目录名前面都加上数字,并创建新的目录.
    # 目录名前面的数字会从当前子目录名开头最大的数字往上递增.
    for dirname in "$@"; do
        mkdir -v "$((++maxnumber))-${dirname}"
    done
}

# 如果所给的第一个选项参数是 -h,则打印帮助信息,然后就退出.当没有
# 提供参数时,也是打印帮助信息,然后退出.要求至少要提供一个参数.
if [ "$1" == "-h" -o $# -eq 0 ]; then
    show_help
    exit 0
fi

# 先保存第一个选项参数的值,避免后面执行 shift 1 后,获取不到这个值.
option_type="$1"
# 在进行后续操作时,必须提供且只提供一个除了 -h 之外的选项参数.
# 下面用 shift 1 移除第一个选项参数,剩下的就全是文件名参数.
shift 1

# 当所给的参数个数等于 0 时,表示只提供了选项参数,没有提供文件名参数.
# 则默认使用 */ 通配符来匹配当前目录下的子目录名. 在 bash 的路径名
# 扩展中, */ 这个写法的通配符表示只扩展为子目录名.目前默认只处理
# 子目录名. 如果想要处理当前目录下的所有文件名,可以把 */ 改成 *.
if [ $# -eq 0 ]; then
    dirnames="*/"
else
    dirnames="$@"
fi

# 为了可以使用 */ 通配符进行扩展,不要用双引号把 ${dir_list} 括起来.
if [ "$option_type" == "-p" ]; then
    prefix_digit_to_filename ${dirnames}
elif [ "$option_type" == "-r" ]; then
    remove_filename_prefix_digit ${dirnames}
elif [ "$option_type" == "-i" ]; then
    increase_filename_number ${dirnames}
elif [ "$option_type" == "-m" ]; then
    # 当创建新的目录时,目录名由用户提供,使用 $@ 获取所给的目录名参数.
    mkdir_digit_filename "$@"
else
    echo "出错: 所给的第一个选项参数不支持,请使用 -h 选项查看帮助信息."
    exit 2
fi

exit